<script setup name="p-table-form">
import { computed } from 'vue'
import Sortable from 'sortablejs'
import { v4 as uuidv4 } from 'uuid'

const { proxy } = getCurrentInstance()

/**
 * 使用此组件 外部需要设置宽度 不能100%宽度 必须写固定宽度 否则会出现宽度无法自适应
 */
const props = defineProps({
  // 列表主键id（必须设置且自动带出）
  keyId: {
    required: false,
    type: String,
    default: 'id'
  },
  // 数据
  data: {
    required: true,
    type: Array,
    default: () => []
  },
  /**
   *  列表每一行
   * @param {
          label:string, 必填
          prop:string,  必填
          showText:boolean,
          width:string, 非必填
          fixed:string, 非必填
           } column
   */
  column: {
    required: true,
    type: Array,
    default: () => []
  },
  // 是否展开
  defaultExpandAll: {
    type: Boolean,
    default: false
  },
  // 拖动class（需要提供本身或上级calss）
  className: {
    type: String,
    default: '',
    required: false
  },
  //   加载状态
  loading: {
    type: Boolean,
    default: false,
    required: false
  },
  //   分页总量
  total: {
    type: String || Number,
    default: 0,
    required: false
  },
  //   高度
  height: {
    type: String,
    default: '40rem',
    required: false
  },
  //   值为空是 显示
  empty: {
    type: String,
    default: '--',
    required: false
  },
  //   是否需要序号
  index: {
    type: Boolean,
    default: true,
    required: false
  },
  //   是否需要复选
  isSel: {
    type: Boolean,
    default: false
  },
  // 是否需要边框
  border: {
    type: Boolean,
    default: true
  },
  // 是否需要合计
  summary: {
    type: Boolean,
    default: false
  },
  // 合计key数组 传入后自动计算
  amountList: {
    type: Array,
    default: () => []
  },
  // 如果需要自己调整合计 请传入summaryMethodYes true
  summaryMethodYes: {
    type: Boolean,
    default: false
  },
  // 合计计算 (自调用 带出一个对象值)
  summaryMethod: {
    type: Function,
    default: () => []
  },
  stripe: {
    type: Boolean,
    default: true
  },
  // 是否需要树结构
  isTree: {
    type: Boolean,
    default: false
  },
  // 树结构列表时 子集
  treeProp: {
    type: Object,
    default: {
      children: 'children',
      hasChildren: 'hasChildren'
    }
  },
  // 是否需要单选（单选必须传入keyId，用作判断）
  isRadio: {
    type: Boolean,
    default: false
  },
  // 是否需要分页
  isPage: {
    type: Boolean,
    default: true
  },
  // 数据总量
  total: {
    required: true,
    type: Number
  },
  // 页数
  page: {
    type: Number,
    default: 1
  },
  // 每页数量
  limit: {
    type: Number,
    default: 20
  },
  // 默认给出选项每页页数
  pageSizes: {
    type: Array,
    default() {
      return [10, 20, 30, 40, 50]
    }
  },
  // 移动端页码按钮的数量端默认值5
  pagerCount: {
    type: Number,
    default: document.body.clientWidth < 992 ? 5 : 7
  },
  // element 官方配置
  layout: {
    type: String,
    default: 'total, sizes, prev, pager, next, jumper'
  },
  // 是否需要背景色
  background: {
    type: Boolean,
    default: true
  },
  // 是否分页后触发回滚
  autoScroll: {
    type: Boolean,
    default: true
  },
  // 是否隐藏
  hidden: {
    type: Boolean,
    default: false
  }
})

// 注册事件名称
const emit = defineEmits(['current-change', 'change', 'listChange'])

// 异常说明
const errorMsg = reactive({
  1: '传入数据类型错误,类型应为数组!',
  2: '传入"column"数据错误,prop属性不能重复!',
  3: '当isRadio启用时,必须传入keyId作为唯一标识!',
  4: `当前keyId为${props.keyId},获取不到主键ID,请传入正确keyId`
})

// 单选时，数据带出
const tableRadio = ref({
  [props.keyId]: ''
})

/**
 * 白名单 判断是否需要 show-overflow-tooltip
 * 一般 操作按钮 开关 标签不需要show-overflow-tooltip 就设置白名单
 * 可设置prop为这些参数,或者设置label同样效果
 */

/**
 *
 */

const whiteList = ref(['x', '操作', '状态'])

// 复选勾选
const selectionChange = (selection, a, b) => {
  try {
    // 判断keyId是否正确
    const resultId = selection.every(t => t[props.keyId])
    if (!resultId) {
      emit('change', {
        ids: [],
        uids: selection.map(item => item['uid']),
        index: selection.map(item => item['index']),
        row: selection
      })
      throw errorMsg['4']
    }
    emit('change', {
      ids: selection.map(item => item[props.keyId]),
      index: selection.map(item => item['index']),
      uids: selection.map(item => item['uid']),
      row: selection
    })
  } catch (err) {
    throw 'p-table组件:' + err
  }
}

// 单选勾选
const currentChange = selection => {
  try {
    // 点击一行时触发
    emit('current-change', { ids: selection[props.keyId], row: selection })
    // 如果不是单选 禁止 以下操作
    if (!props.isRadio) return
    // 判断keyId是否正确
    if (props.isRadio) {
      if (!selection[props.keyId]) {
        throw errorMsg['3']
      }
    }
    // 给单选框一个值
    tableRadio.value[props.keyId] = selection[props.keyId]
    emit('change', { ids: selection[props.keyId], row: selection })
  } catch (err) {
    throw 'p-table组件:' + err
  }
}

// 筛选 是否有重复 prop
const tableColumn = computed(() => {
  const newData = Array.from(new Set(props.column.map(t => t.prop)))
  if (props.column.length > newData.length) {
    throw 'p-table组件:' + errorMsg['2']
  } else {
    return props.column
  }
})

// 做一些东西 数据为空的时候做出显示 给予提示
const getData = (val, key, label) => {
  try {
    if (val && key && val[key] != null && val[key] != undefined) {
      return val[key]
    } else {
      if (props.empty) {
        return props.empty
      } else if (!key) {
        return `" ${label} " 的 "prop" 为空`
      } else {
        return `" ${label} "为空, ${key} -> " ${val[key]} "`
      }
    }
  } catch (err) {
    throw 'p-table组件:' + err
  }
}

// 判断是否类型错误
const list = computed(() => {
  if (!Array.isArray(props.data)) {
    throw 'p-table组件:' + errorMsg['1']
  } else {
    // 单选时,数据刷新默认选中一项
    if (props.data.length > 0 && props.isRadio) {
      // 每次置空
      tableRadio.value[props.keyId] = ''
      const obj = props.data[0]
      // 如果为空 抛出异常
      if (!obj[props.keyId]) {
        throw 'p-table组件:' + errorMsg['4']
      }
      // 单选时，默认提供一个唯一标识
      tableRadio.value[props.keyId] = obj[props.keyId]
    }
    // 数据参入uuid
    props.data.forEach((t, ind) => {
      t[`uid`] = uuidv4()
      t['index'] = ind
    })

    return props.data || []
  }
})

// 表格拖动

// 创建拖拽实例
const initSort = () => {
  if (!props.className) return
  console.log(props.className, 'props.className')
  const table = document.querySelector(`.${props.className} .el-table__body-wrapper tbody`)
  Sortable.create(table, {
    group: 'shared',
    animation: 150,
    easing: 'cubic-bezier(1, 0, 0, 1)',
    onStart: () => {},
    // 结束拖动事件
    onEnd: async ({ newIndex, oldIndex }) => {
      setNodeSort(list.value, oldIndex, newIndex)
    }
  })
}

// 拖拽完成修改数据排序
const setNodeSort = (data, oldIndex, newIndex) => {
  const currRow = data.splice(oldIndex, 1)[0]
  data.splice(newIndex, 0, currRow)

  emit('listChange', list.value)
}

// 校验表格
const validate = async () => {
  const elements = await proxy.$refs.itemFormRef
  if (!Array.isArray(elements))
    return console.log('%cp-table-form组件:列表数据错误 => ' + typeof elements, 'color:red;font-size:14px')
  elements.forEach(t => t.validate())
  return new Promise(async (resolve, reject) => {
    try {
      for (let index = 0; index < elements.length; index++) {
        const v = await elements[index].validate()
      }

      resolve(true)
    } catch {
      proxy.warningMsg('列表校验未通过,请注意查看!')
      reject('校验表格err')
    }
  })
}
onMounted(() => {
  if (props.isTree) return
  initSort()
})

const a = async () => {
  await validate()
}

defineExpose({
  validate
})
</script>

<template>
  <div class="p-table">
    <el-button @click="a">开发环境专用(查看是否校验通过)</el-button>
    <el-table
      class="tableClass"
      :row-key="props.keyId"
      :height="props.height"
      :stripe="props.stripe"
      :border="props.border"
      :data="list"
      @selection-change="selectionChange"
      @current-change="currentChange"
    >
      <el-table-column v-if="isSel && !isRadio" type="selection" width="55" align="center" fixed="left" />
      <el-table-column v-if="isRadio" width="70" fixed="left" align="center">
        <template #default="{ row }">
          <el-radio-group v-if="row[props.keyId]" v-model="tableRadio[props.keyId]">
            <el-radio :label="row[props.keyId]">&nbsp;</el-radio>
          </el-radio-group>
        </template>
      </el-table-column>
      <el-table-column v-if="index" label="序号" type="index" width="70" fixed="left" align="center" />
      <template v-for="(columnItem, index) in tableColumn" :key="`p-table-form-${columnItem.prop}-${index}`">
        <!--  `${(80 / tableColumn.length) * (10 + columnItem.label)}` -->
        <el-table-column
          :fixed="columnItem.fixed"
          :label="columnItem.label"
          :prop="columnItem.prop"
          :rules="columnItem.rules"
          :width="
            columnItem.width !== undefined
              ? columnItem.width
              : columnItem.label.includes('单号') ||
                columnItem.label.includes('编码') ||
                columnItem.label.includes('名称') ||
                columnItem.label.includes('规格') ||
                columnItem.label.includes('入库') ||
                columnItem.label.includes('计划')
              ? '200'
              : columnItem.label == '操作'
              ? '300'
              : ''
          "
          :show-overflow-tooltip="columnItem.showText"
        >
          <template #header>
            <div>
              <span style="color: red" v-if="columnItem.required">*</span>
              <span> {{ columnItem.label }}</span>
            </div>
          </template>
          <!--   -->
          <template #default="scope">
            <el-form scroll-to-error class="formClass" ref="addFormRef" :model="scope.row" label-width="0px" inline>
              <el-form-item
                ref="itemFormRef"
                class="itemForm"
                label=""
                :rules="
                  columnItem.rules || [
                    {
                      required: columnItem.required,
                      message: `${columnItem.label}为必填`,
                      trigger: columnItem.type == '输入框' ? 'blur' : 'change'
                    }
                  ]
                "
                :prop="columnItem.required ? columnItem.prop : null"
              >
                <slot
                  name="columnCell"
                  :record="scope.row"
                  :scope="scope"
                  :column="{
                    prop: columnItem.prop,
                    label: columnItem.label,
                    width: columnItem.width || '200'
                  }"
                >
                  <!-- <el-input
                    v-if="columnItem.type == '输入框'"
                    clearable
                    :disabled="columnItem.disabled"
                    :placeholder="`请填写${columnItem.label}`"
                    v-model="scope.row[columnItem.prop]"
                  />
                  <el-input-number
                    v-else-if="columnItem.type == '数字输入框'"
                    clearable
                    :min="0"
                    :max="99999"
                    :disabled="columnItem.disabled"
                    :placeholder="`请填写${columnItem.label}`"
                    v-model="scope.row[columnItem.prop]"
                  />
                  <p-select
                    v-else-if="columnItem.type == '下拉框'"
                    clearable
                    :disabled="columnItem.disabled"
                    :placeholder="`请选择${columnItem.label}`"
                    v-model="scope.row[columnItem.prop]"
                    :option="
                      columnItem.option || columnItem.opt || columnItem.t
                    "
                  />
                  <el-date-picker
                    v-else-if="columnItem.type == '日期'"
                    :disabled="columnItem.disabled"
                    :placeholder="`请选择${columnItem.label}`"
                    v-model="scope.row[columnItem.prop]"
                    value-format="YYYY-MM-DD"
                    clearable
                  /> -->
                  {{ getData(scope.row, columnItem.prop, columnItem.label) }}
                </slot>
              </el-form-item>
            </el-form>
          </template>
        </el-table-column>
      </template>
    </el-table>
  </div>
</template>
<style scoped lang="scss">
.p-table {
  width: 100%;
}

.p-table ::v-deep .el-table__cell {
  // padding: 0 !important;
}
.formClass {
  overflow: hidden;
}
.itemForm {
  width: 100% !important;
}

.tableClass {
  width: 100% !important;
  margin-top: 0.9375rem;
}
</style>
